import { type ConversationMessage } from "@/types/conversation";
import { useConversationContext } from "@/components/ConversationProvider";
import { useEffect, useId, useMemo } from "react";
import {
  filterEmptyMessages,
  mergeMessages,
  sortByCreatedAt,
  useConversationStore,
} from "@/stores/conversationStore";

/**
 * Text mode for conversation display
 */
export type TextMode = "llm" | "tts";

/**
 * Options for `usePipecatConversation`.
 */
interface Props {
  /**
   * Optional callback invoked whenever a new message is added or finalized.
   * This callback will be called with the latest message object.
   */
  onMessageAdded?: (message: ConversationMessage) => void;
  /**
   * Text mode for displaying assistant messages.
   * - "llm": Shows text as it's generated by the LLM
   * - "tts": Shows text as it's spoken by the TTS
   * @default "llm"
   */
  textMode?: TextMode;
}

/**
 * React hook for accessing and subscribing to the current conversation stream.
 *
 * This hook provides:
 * - The current list of conversation messages, ordered and merged for display.
 * - An `injectMessage` function to programmatically add a message to the conversation.
 * - The ability to register a callback (`onMessageAdded`) that is called whenever a new message is added or finalized.
 *
 * Internally, this hook:
 * - Subscribes to conversation state updates and merges/filters messages for UI consumption.
 * - Ensures the provided callback is registered and unregistered as the component mounts/unmounts or the callback changes.
 *
 * @param {Props} [options] - Optional configuration for the hook.
 * @returns {{
 *   messages: ConversationMessage[];
 *   injectMessage: (message: { role: "user" | "assistant" | "system"; parts: any[] }) => void;
 * }}
 */
export const usePipecatConversation = ({
  onMessageAdded,
  textMode = "llm",
}: Props = {}) => {
  const { injectMessage } = useConversationContext();
  const { registerMessageCallback, unregisterMessageCallback } =
    useConversationStore();

  // Generate a unique ID for this hook instance
  const callbackId = useId();

  // Register and unregister the callback
  useEffect(() => {
    // Register the callback for message updates
    registerMessageCallback(callbackId, onMessageAdded);

    // Cleanup: unregister when component unmounts or callback changes
    return () => {
      unregisterMessageCallback(callbackId);
    };
  }, [
    callbackId,
    onMessageAdded,
    registerMessageCallback,
    unregisterMessageCallback,
  ]);

  // Get the raw state from the store using separate selectors
  const messages = useConversationStore((state) => state.messages);
  const llmTextStreams = useConversationStore((state) => state.llmTextStreams);
  const ttsTextStreams = useConversationStore((state) => state.ttsTextStreams);

  // Memoize the filtered messages to prevent infinite loops
  const filteredMessages = useMemo(() => {
    // First, create messages with the appropriate text streams
    const messagesWithTextStreams = messages.map((message) => {
      if (message.role === "assistant") {
        const messageId = message.createdAt; // Use createdAt as unique ID
        const textStream =
          textMode === "llm"
            ? llmTextStreams.get(messageId) || ""
            : ttsTextStreams.get(messageId) || "";

        return {
          ...message,
          parts: textStream
            ? [
                {
                  text: textStream,
                  final: message.final || false,
                  createdAt: message.createdAt,
                },
              ]
            : message.parts,
        };
      }
      return message;
    });

    // Then process the messages normally
    const processedMessages = mergeMessages(
      filterEmptyMessages(messagesWithTextStreams.sort(sortByCreatedAt)),
    );

    return processedMessages;
  }, [messages, llmTextStreams, ttsTextStreams, textMode]);

  return {
    messages: filteredMessages,
    injectMessage,
  };
};

/**
 * @deprecated Use `usePipecatConversation` instead. This alias will be removed in a future major release.
 */
export const useConversation = usePipecatConversation;
export default usePipecatConversation;
